/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun<iot25@lingyun>
 *                  All rights reserved.
 *
 *       Filename:  socket_server_thread.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(18/03/25)
 *         Author:  LingYun <iot25@lingyun>
 *      ChangeLog:  1, Release initial version on "18/03/25 18:07:05"
 *                 
 ********************************************************************************/
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<getopt.h>
#include<pthread.h>
#include<ctype.h>
/*
命令行参数解析：	通过getopt_long函数解析命令行参数，获取服务器监听的端口号。
创建套接字：		使用socket函数创建一个TCP套接字。
设置套接字选项：	使用setsockopt函数设置SO_REUSEADDR选项，允许地址重用。
绑定套接字：		使用bind函数将套接字绑定到指定的端口。
监听连接：			使用listen函数开始监听连接请求。
接受连接：			在无限循环中使用accept函数接受客户端连接请求。
创建线程：			为每个新的客户端连接创建一个新线程，使用pthread_create函数。*/

typedef void *(THREAD_BODY) (void *thread_arg);
void *thread_worker(void *ctx);//线程工作函数。

int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg);

void printf_usage(char *program)
{
	printf(" %s usage: \n",program);
	printf(" -p(--port): sepcify server listen port.\n");
	printf(" -h(--help): printf this help information.\n");

	return;
}

int main(int argc, char *argv[])
{
	int 						sockfd = -1;
	int 						rv = -1;
	struct sockaddr_in			servaddr;
	struct sockaddr_in			cliaddr;
	socklen_t					len;
	int 						port = 0;
	int 						clifd;
	int 						ch;
	int 						on = -1;
	pthread_t					tid;


	struct option 				opts[] = {
		{"port", required_argument, NULL, 'p'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}	
};

while( (ch = getopt_long(argc, argv, "p:h", opts,NULL)) !=-1)//函数解析命令行参数
{
	switch(ch)
	{
		case 'p':    //p 指定端口
			port=atoi(optarg);
			break;

		case 'h':  //打印帮助信息
			printf_usage(argv[0]);
			return 0;
	}
}

if( !port )//如果用户没有指定端口号，则打印帮助信息并退出程序。
{

	printf_usage(argv[0]);
	return 0;
}

sockfd = socket(AF_INET, SOCK_STREAM,0);//创建一个 TCP 套接字，AF_INET 表示 IPv4 地址族，SOCK_STREAM 表示流式套接字（TCP）
if(sockfd<0)
{
	printf(" Create socket failure: %s\n",strerror(errno));
	return -1;
}
printf("Create socket[%d] successfully!\n",sockfd);

setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//使用setsockopt函数设置SO_REUSEADDR选项，允许地址重用。

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//设置服务器监听的 IP 地址为 INADDR_ANY，表示监听所有可用的 IP 地址。

rv = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//使用bind函数将套接字绑定到指定的端口和IP地址。
if( rv<0 )
{
	printf("socket[%d] bind on port[%d] failure: %s\n",socket, port, strerror(errno));
	return -2;
}
listen(sockfd,13);//监听连接连接请求。
printf("Start to listen on port [%d]\n",port);

while(1)
{
	printf(" Start accept new client incoming...\n");
	
	clifd=accept(sockfd, (struct sockaddr *)&cliaddr, &len);//while循环中使用accept函数接受客户端连接。
	if( clifd<0)
	{
		printf("Accept new client failure: %s\n",strerror(errno));
		continue;
	}

	printf(" Accept new client successfully\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));

	thread_start(&tid, thread_worker, (void *)clifd);//启动一个新线程来处理客户端请求，传递 clifd 作为线程参数。
}
	close(sockfd);
	return 0;
}

int thread_start(pthread_t * thread_id,THREAD_BODY * thread_workbody, void *thread_arg)//thread_start函数用于创建线程，设置线程属性，包括栈的大小和分离状态
{
	int							 rv = -1;
	pthread_attr_t				 thread_attr;// 线程属性

	if( pthread_attr_init(&thread_attr)) // 初始化线程属性
	{
		printf("pthread_attr_init() failure: %s\n", strerror(errno));
		goto CleanUp;
	}

	if(pthread_attr_setstacksize(&thread_attr, 120*1024))// 设置线程栈大小
	{
		printf("pthread_attr_setstacksize() failure: %s\n",strerror(errno));
		goto CleanUp;
	}

	if(pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED))// 设置线程为分离状态，线程结束后自动释放资源
	{
		printf("pthread_attr_setdetachstate() failure: %s\n",strerror(errno));
		goto CleanUp;
	}

	if(pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg))// 创建线程
	{
		printf("Create thread failure: %s\n", strerror(errno));
		goto CleanUp;
	}

	rv=0;

CleanUp:
//销毁线程的属性。
	pthread_attr_destroy(&thread_attr);
	return rv;
}
void *thread_worker(void *ctx)
{
	int 					clifd;// 客户端套接字描述符
	int 					rv;// 返回值
	char					buf[1024];
	int						i;

	if( !ctx ) // 检查传入的参数是否有效
	{
		printf("invalid input arguments in %s()\n",__FUNCTION__);
		pthread_exit(NULL);
	}

	clifd = (int)ctx;// 获取客户端套接字描述符 

	printf("Child thread start to commuicate with sockt client...\n");

	while(1)
	{
		memset(buf, 0, sizeof(buf));
		rv = read(clifd,buf,sizeof(buf));
		if(rv<0)
		{
			printf(" Read data form client sockfd[%d] failure: %s and thread will exit\n",clifd, strerror(errno));
			close(clifd);
			pthread_exit(NULL);
		}
		else if(rv == 0)
		{
			printf(" Socket{%d] get disconnected and thread will exit.\n",clifd);
		}
		else if( rv>0 )
		{
			printf("Read %d bytes data from Server: %s\n",rv ,buf);
		}

		for(i-0; i<rv; i++)
		{
			buf[i]= toupper(buf[1]);//toupper函数将小写转成大写。
		}

		rv=write(clifd,buf,rv);
		if(rv<0)
		{
			printf(" Write to client by socket[%d] failure: %s and thread will exit\n",clifd, strerror(errno));
			close(clifd);
			pthread_exit(NULL);
		}
	}
}




